package com.gitee.wsl.collections.iterator

import com.gitee.wsl.base.Objects


/**
 * This iterator creates a Cartesian product of the input iterables,
 * equivalent to nested for-loops.
 *
 *
 * The iterables provided to the constructor are used in reverse order, each
 * until exhaustion before proceeding to the next element of the prior iterable
 * and repeating. Consider the following example:
 *
 * <pre>`List<Character> iterable1 = Arrays.asList('A', 'B', 'C');
 * List<Character> iterable2 = Arrays.asList('1', '2', '3');
 * CartesianProductIterator<Character> it = new CartesianProductIterator<>(
 * iterable1,
 * iterable2);
 * while (it.hasNext()) {
 * List<Character> tuple = it.next();
 * System.out.println(tuple.get(0) + ", " + tuple.get(1));
 * }
`</pre> *
 *
 *
 * The output will be:
 *
 * <pre>
 * A, 1
 * A, 2
 * A, 3
 * B, 1
 * B, 2
 * B, 3
 * C, 1
 * C, 2
 * C, 3
</pre> *
 *
 *
 * The `remove()` operation is not supported, and will throw an
 * `UnsupportedOperationException`.
 *
 *
 *
 * If any of the input iterables is empty, the Cartesian product will be empty.
 * If any of the input iterables is infinite, the Cartesian product will be
 * infinite.
 *
 *
 * @param <E> the type of the objects being permuted
 * @since 4.5.0-M3
</E> */
class CartesianProductIterator<E>(vararg iterables: Iterable<E>) : Iterator<List<E>> {
    /**
     * The iterables to create the Cartesian product from.
     */
    private val iterables = ArrayList<Iterable<E>>(iterables.size)

    /**
     * The iterators to generate the Cartesian product tuple from.
     */
    private val iterators = ArrayList<Iterator<E>>(iterables.size)

    /**
     * The previous generated tuple of elements.
     */
    private var previousTuple: MutableList<E>? = null

    /**
     * Constructs a new `CartesianProductIterator` instance with given iterables.
     *
     * @param iterables the iterables to create the Cartesian product from
     * @throws NullPointerException if any of the iterables is null
     */
    init {
        for (iterable in iterables) {
            this.iterables.add(iterable)
            val iterator = iterable.iterator()
            if (!iterator.hasNext()) {
                iterators.clear()
                break
            }
            iterators.add(iterator)
        }
    }

    /**
     * Returns `true` if the iteration has more elements.
     *
     * @return true if there are more tuples, otherwise false
     */
    override fun hasNext(): Boolean {
        return iterators.any { it.hasNext() }
    }

    /**
     * Returns the next tuple of the input iterables.
     *
     * @return a list of the input iterables' elements
     * @throws NoSuchElementException if there are no more tuples
     */
    public override fun next(): List<E> {
        if (!hasNext()) {
            throw NoSuchElementException()
        }
        if (previousTuple == null) {
            previousTuple = ArrayList(iterables.size)
            for (iterator in iterators) {
                previousTuple!!.add(iterator.next())
            }
            return ArrayList(previousTuple!!)
        }
        for (i in iterators.size - 1 downTo 0) {
            var iterator: Iterator<E> = iterators[i]
            if (iterator.hasNext()) {
                previousTuple!![i] = iterator.next()
                return ArrayList(previousTuple!!)
            }
            iterator = iterables[i].iterator()
            iterators[i] = iterator
            previousTuple!![i] = iterator.next()
        }
        throw IllegalStateException("reached unreachable code")
    }

//    public override fun remove() {
//        throw UnsupportedOperationException("remove")
//    }
}
